package monitor

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/shirou/gopsutil/cpu"
	"github.com/shirou/gopsutil/disk"
	"github.com/shirou/gopsutil/mem"
	"math"
	"os"
	"runtime"
	"ruoyi-go/utils"
	"strings"
	"time"
)

// CpuInfo struct to hold CPU related data
type CpuInfo struct {
	CpuNum  int     `json:"cpuNum"`
	Total   float64 `json:"total"`
	SysInfo float64 `json:"sys"`
	Used    float64 `json:"used"`
	Wait    float64 `json:"wait"`
	Free    float64 `json:"free"`
}

// MemInfo struct to hold memory related data
type MemInfo struct {
	Total float64 `json:"total"`
	Used  float64 `json:"used"`
	Free  float64 `json:"free"`
	Usage float64 `json:"usage"`
}

// JvmInfo struct to hold JVM related data
type JvmInfo struct {
	Total     float64 `json:"total"`
	Max       float64 `json:"max"`
	Free      float64 `json:"free"`
	Version   string  `json:"version"`
	Home      string  `json:"home"`
	StartTime string  `json:"startTime"`
	Usage     float64 `json:"usage"`
	Used      float64 `json:"used"`
	InputArgs string  `json:"inputArgs"`
	RunTime   string  `json:"runTime"`
	Name      string  `json:"name"`
}

// SysInfo struct to hold system information
type SysInfo struct {
	ComputerName string `json:"computerName"`
	ComputerIp   string `json:"computerIp"`
	OsName       string `json:"osName"`
	OsArch       string `json:"osArch"`
	UserDir      string `json:"userDir"`
}

// SysFileInfo struct to hold information about disk files
type SysFileInfo struct {
	DirName     string  `json:"dirName"`
	SysTypeName string  `json:"sysTypeName"`
	TypeName    string  `json:"typeName"`
	Total       string  `json:"total"`
	Free        string  `json:"free"`
	Used        string  `json:"used"`
	Usage       float64 `json:"usage"`
}

// ServerInfo struct encapsulates all server information
type ServerInfo struct {
	CpuInfo  CpuInfo       `json:"cpu"`
	MemInfo  MemInfo       `json:"mem"`
	JvmInfo  JvmInfo       `json:"jvm"`
	SysInfo  SysInfo       `json:"sys"`
	SysFiles []SysFileInfo `json:"sysFiles"`
}

const OshiWaitSecond = 1000 * time.Millisecond

// 程序启动时间
var programStartTime time.Time

func GetServerInfoII(context *gin.Context) (server ServerInfo, err error) {

	// 调用 CopyTo 方法填充 server 的信息
	err = server.CopyTo(context)
	if err != nil {
		return
	}
	return server, err
}

// CopyTo retrieves server information
func (s *ServerInfo) CopyTo(context *gin.Context) error {
	// Fetching CPU info
	if err := s.setCpuInfo(); err != nil {
		return err
	}

	// Fetching memory info
	if err := s.setMemInfo(); err != nil {
		return err
	}

	// Fetching system info
	if err := s.setSysInfo(); err != nil {
		return err
	}

	// Fetching JVM info
	if err := s.setJvmInfo(); err != nil {
		return err
	}

	// Fetching disk info
	if err := s.setSysFiles(); err != nil {
		return err
	}

	return nil
}

// Set CPU information
func (s *ServerInfo) setCpuInfo() error {
	// Get the number of logical processors
	cpuNum, err := cpu.Counts(true)
	if err != nil {
		return err
	}

	// Get the CPU load ticks (user, system, idle, etc.)
	// First we get the previous ticks
	prevTicks, err := cpu.Times(false)
	if err != nil {
		return err
	}

	// Sleep for the specified interval
	time.Sleep(OshiWaitSecond)

	// Then we get the new ticks
	ticks, err := cpu.Times(false)
	if err != nil {
		return err
	}

	// Calculate the difference in ticks between the two readings
	if len(prevTicks) > 0 && len(ticks) > 0 {
		prev := prevTicks[0]
		curr := ticks[0]

		//s.CpuInfo.SysInfo = curr.System - prev.System
		//s.CpuInfo.Used = curr.User - prev.User
		//s.CpuInfo.Wait = curr.Iowait - prev.Iowait

		sysInfo := curr.System - prev.System
		used := curr.User - prev.User
		wait := curr.Iowait - prev.Iowait
		free := curr.Idle - prev.Idle
		// Total CPU usage
		s.CpuInfo.Total = used + sysInfo + wait + free

		s.CpuInfo.SysInfo = math.Round((sysInfo/s.CpuInfo.Total)*10000) / 100
		s.CpuInfo.Used = math.Round((used/s.CpuInfo.Total)*10000) / 100
		s.CpuInfo.Wait = math.Round((wait/s.CpuInfo.Total)*10000) / 100
		s.CpuInfo.Free = math.Round((free/s.CpuInfo.Total)*10000) / 100
	}
	s.CpuInfo.CpuNum = cpuNum
	return nil
}

// Set memory information
func (s *ServerInfo) setMemInfo() error {
	vmStat, err := mem.VirtualMemory()
	if err != nil {
		return err
	}

	// Convert uint64 to int64 explicitly
	total := float64(vmStat.Total)
	used := float64(vmStat.Used)
	free := float64(vmStat.Free)
	s.MemInfo.Total = math.Round(total/(1024*1024*1024)*100) / 100
	s.MemInfo.Used = math.Round(used/(1024*1024*1024)*100) / 100
	s.MemInfo.Free = math.Round(free/(1024*1024*1024)*100) / 100
	s.MemInfo.Usage = math.Round(used/total*10000) / 100

	return nil
}

// Set system information
func (s *ServerInfo) setSysInfo() error {
	s.SysInfo.ComputerName, _ = os.Hostname()
	s.SysInfo.ComputerIp = "127.0.0.1" // Placeholder for actual IP, can use third-party package to get real IP
	s.SysInfo.OsName = runtime.GOOS
	s.SysInfo.OsArch = runtime.GOARCH
	s.SysInfo.UserDir, _ = os.Getwd()

	return nil
}

// Set JVM information
func (s *ServerInfo) setJvmInfo() error {
	var m runtime.MemStats
	runtime.ReadMemStats(&m)

	// 正确的字段名称
	total := int64(m.TotalAlloc)
	max := int64(m.HeapSys)
	free := int64(m.HeapIdle)
	s.JvmInfo.Total = GetTotal(total) // Go运行时已分配的总内存
	s.JvmInfo.Max = GetMax(max)       // Go运行时最大堆内存
	s.JvmInfo.Free = GetFree(free)    // Go堆中空闲的内存
	s.JvmInfo.Version = runtime.Version()
	s.JvmInfo.Home = runtime.GOROOT()
	s.JvmInfo.StartTime = GetStartTime()

	s.JvmInfo.Usage = GetUsage(total, free)
	s.JvmInfo.Used = GetUsed(total, free)
	s.JvmInfo.InputArgs = GetInputArgs()
	s.JvmInfo.RunTime = GetRunTime()
	s.JvmInfo.Name = GetName()

	//"startTime": "2025-01-07 02:19:54",
	//	"usage": 46.17,
	//	"used": 200.84,
	//	"inputArgs": "[]",
	//	"runTime": "3天0小时36分钟",
	//	"name": "OpenJDK 64-Bit Server VM"
	return nil
}

// Set disk file system information
func (s *ServerInfo) setSysFiles() error {
	partitions, err := disk.Partitions(true)
	if err != nil {
		return err
	}

	for _, partition := range partitions {
		usage, err := disk.Usage(partition.Mountpoint)
		if err != nil {
			return err
		}

		sysFile := SysFileInfo{
			DirName:     partition.Mountpoint,
			SysTypeName: partition.Fstype,
			TypeName:    partition.Device,
			Total:       convertFileSize(usage.Total),
			Free:        convertFileSize(usage.Free),
			Used:        convertFileSize(usage.Used),
			Usage:       float64(usage.Used) / float64(usage.Total) * 100,
		}

		s.SysFiles = append(s.SysFiles, sysFile)
	}

	return nil
}

// Convert file size to readable format
func convertFileSize(size uint64) string {
	kb := uint64(1024)
	mb := kb * 1024
	gb := mb * 1024

	if size >= gb {
		return fmt.Sprintf("%.1f GB", float64(size)/float64(gb))
	} else if size >= mb {
		return fmt.Sprintf("%.1f MB", float64(size)/float64(mb))
	} else if size >= kb {
		return fmt.Sprintf("%.1f KB", float64(size)/float64(kb))
	} else {
		return fmt.Sprintf("%d B", size)
	}
}

// Arithmetic operations
func Div(dividend, divisor float64, precision int) float64 {
	if divisor == 0 {
		return 0
	}
	scale := math.Pow(10, float64(precision))
	return math.Round((dividend/divisor)*scale) / scale
}

func Mul(a, b float64) float64 {
	return a * b
}

// 获取已使用的内存 (单位: MB)
func GetUsed(total, free int64) float64 {
	return Div(float64(total-free), 1024*1024, 2)
}

// 获取已使用的内存 (单位: MB)
func GetTotal(total int64) float64 {
	return Div(float64(total), 1024*1024, 2)
}

// 获取已使用的内存 (单位: MB)
func GetMax(max int64) float64 {
	return Div(float64(max), 1024*1024, 2)
}

// 获取已使用的内存 (单位: MB)
func GetFree(free int64) float64 {
	return Div(float64(free), 1024*1024, 2)
}

// 获取内存使用率 (百分比)
func GetUsage(total, free int64) float64 {
	return Mul(Div(float64(total-free), float64(total), 4), 100)
}

// 获取JDK名称 (Go等价为获取运行时版本信息)
func GetName() string {
	return runtime.Version()
}

// 初始化程序启动时间
func init() {
	programStartTime = time.Now()
}

// 获取程序启动时间
func GetStartTime() string {
	return programStartTime.Format("2006-01-02 15:04:05")
}

// 获取JDK运行时间 (Go等价为程序运行时间)
func GetRunTime() string {
	return utils.TimeDistance(utils.GetNowDate(), programStartTime)
}

// 获取运行参数 (Go等价为获取命令行参数)
func GetInputArgs() string {
	return strings.Join(os.Args, " ")
}
